
****************************************************************************
*	2500 A.D. 6502 C COMPILER RUN-TIME STARTUP ROUTINE                 *
****************************************************************************

		.linklist
		.options  dch

;	Run-time startup conditional assembly definitions.

equate_defintions:	.section  offset 0, ref_only  ;all 'equates' section

NO:			.equal	0		;define no
YES:			.equal	.not.NO		;define yes

;	If the startup routine is going to be used with the 2500 A.D. simulator
;	then it is common to set a breakpoint at the label 'end'.  If the I/O
;	is the interrupt driven serial driver, the output buffer may not be 
;	empty when this breakpoint is reached, and since the simulator generates
;	interrupts based on machine cycle counts, the final results may not be
;	output.
;	Set the following to YES to assemble the code that checks to see if
;	the output buffer is empty before reaching the label 'end'.  This will
;	cause all of the results to be output before the simulator will stop.

SIMULATOR:		.equal	NO

;	Set the 6502 stack.

STACK:			.equal	ffh		;top of 6502 stack
STACK_PAGE:		.equal	100h		;page stack resides in

;	Set the compiler function stack.  This is normally located at the
;	top of RAM.

			.iftrue	SIMULATOR=NO
COMPILER_STACK:		.equal	cfffh		;top of compiler stack
			.else
COMPILER_STACK:		.equal	fff9h
			.endif

;	If bank switching is going to be used, then set the following to
;	YES to include the bank switching code.

BANK_SWITCHING:		.equal	NO

*******************************************************************************
*	The following settings are for the 2500 A.D. monitor and test board.  *
*	In most cases, they should be set to NO.                              *
*******************************************************************************

;	There is a simple interface that allows test routines to use the I/O
;	drivers in the monitor that runs on the 2500 A.D. test board.  To 
;	assemble this in, set the following to YES, otherwise set it to NO.
;	The only time you would want to include this section is if you have
;	a similar situation, where programs are loaded down to a board and
;	run, and they can use the I/O drivers in the controlling program.
;	The entry point for this interface is called 'c6502rt_entry'.  The
;	functions are in the routine 'c6502rt_functions'.

MONITOR_INTERFACE:		.equal	NO

;	There is a 74610 memory mapper test and a ram test included in this
;	startup file.  These tests are for the 2500 A.D. test board, and almost
;	certainly will have to be modified for other hardware.  Set the
;	following to YES to run these tests, otherwise set the following to
;	NO.

MEMORY_TEST:			.equal	NO

*******************************************************************************
*	Define sections that will be used.  Doing it here generates the       *
*	proper linking order.                                                 *
*******************************************************************************

vectors:		.section	;restart & interrupt vectors section
vectors_addr:		.equal	$
page0:			.section  page0	;page 0 section
page0_addr:		.equal	$
bank_table:		.section	;bank table section
bank_table_addr:	.equal	$
program:		.section	;program section
prog_addr:		.equal	$
runtime_program:	.section	;start of runtime support section
runtime_program_addr:	.equal	$
lib_program:		.section	;library program section
lib_program_addr:	.equal	$
const_data:		.section	;constant data section
const_data_addr: 	.equal	$
lib_const_data:		.section	;library constant data
lib_const_data_addr:	.equal	$
const_data_end:		.section	;end of program & constant data
const_data_end_addr:	.equal	$
init_data:		.section	;initialized data section
init_data_addr:		.equal	$
runtime_init_data:	.section	;runtime initialized data section
runtime_init_data_addr:	.equal	$
lib_init_data:		.section	;library init data section
lib_init_data_addr:	.equal	$
init_data_end:		.section	;end of all initialized data
init_data_end_addr:	.equal	$
uninit_data:		.section	;uninitialized data section
uninit_data_addr: 	.equal	$
uninit_data_end: 	.section	;end of all uninitialized data
uninit_data_end_addr: 	.equal	$

;	The following sections are used to generate offsets for the library
;	temporary storage locations.  They are actually on the stack, with
;	each location referenced as an offset from the value stored in
;	'__lib_temp_ptr'.
;
;	THESE ARE CALCULATED AUTOMATICALLY AND SHOULD NOT BE MODIFIED,
;	ESPECIALLY THE LINKER CONTROL MODIFIERS.

lib_temp_constants:	.section	offset 0, ref_only
lib_temp_constants_end:	.section	stacked,  ref_only
lib_temp_constants_size: .equal	$	;size of temporary constants

;	Put memory test code down in low half of ROM.  After the test, it is
;	mapped out.

.iftrue	MEMORY_TEST=YES
memory_test_section:	.section	offset 1000h
memory_test_2000h:	.section	offset 2000h
.endif

*******************************************************************************
*	End of Section definitions                                            *
*******************************************************************************

map_8_11:	.equal	0200h		;memory mapper bits 8 - 11 addr
map_addr:	.equal	0210h		;memory mapper address
map_enable:	.equal	0220h		;memory mapper enable address
serial_control:	.equal	0233h		;address of rate & size control
serial_command:	.equal	0232h		;address of command register
serial_status:	.equal	0231h		;addr of xmit & receive status reg
rxd:		.equal	0230h		;receiver register
txd:		.equal	0230h		;transmitter register
rxd_ready:	.equal	08h		;receiver ready bit mask
txd_ready:	.equal	10h		;transmitter ready bit mask
rxd_int_enable:	.equal	02h		;receiver interrupt enable bit mask
txd_int_enable:	.equal	04h		;transmitter interrupt enable bit mask
rxd_error:	.equal	07h		;frame, overrun & parity error flags
CR:		.equal	0dh
LF:		.equal	0ah
BS:		.equal	08h
XON:		.equal	'Q'-40h		;xon code
XOFF:		.equal	'S'-40h		;xoff code
CTRL_C:		.equal	'C'-40h		;control c
INPUT_BUF_SIZE:	.equal	256		;size of input buffer
OUTPUT_BUF_SIZE: .equal	256		;size of output buffer

		.page0

		.global	__oper1
		.global	__oper2
		.global	__addr_reg
		.global	__stack_ptr
		.global	__lib_temp_ptr
		.global	__temp_a_reg     
		.extern	_main
		.extern	__indirect_call

		.iftrue	BANK_SWITCHING=YES
		.global	__oper1_high8
		.global	__oper2_high8
		.endif

__oper1:	.word			;operand #1 storage (MUST BE IN PAGE 0)
		.iftrue	BANK_SWITCHING=YES
__oper1_high8:	.byte			;oper #1 bits 23-16 (MUST BE IN PAGE 0)
		.endif
__oper2:	.word			;operand #2 storage (MUST BE IN PAGE 0)
		.iftrue	BANK_SWITCHING=YES
__oper2_high8:	.byte			;oper #2 bits 23-16 (MUST BE IN PAGE 0)
		.endif
__addr_reg:	.word			;address register (MUST BE IN PAGE 0)
__stack_ptr:	.word			;compiler stack ptr (MUST BE IN PAGE 0)
__lib_temp_ptr:	.word			;ptr to library temporary storage
					; (MUST BE IN PAGE 0)
__temp_a_reg:	.byte			;temp storage for A reg, used by
                                        ;__interrupt_exit routine 
                                        ;(MUST BE IN PAGE 0)

		.uninit_data

input_buf:	.ds	INPUT_BUF_SIZE	;allocate input buffer
output_buf:	.ds	OUTPUT_BUF_SIZE	;allocate output buffer

		.init_data

		.global	__raw_mode
		.global	_ctrl_c_signal

__raw_mode:	.byte	0		;enable all input character processing
_ctrl_c_signal:	.word	ctrl_c_reset	;control c interrupt signal routine

output_buf_empty: .byte	1		;initialize output buffer empty flag
input_buf_ptr:	.word	input_buf	;initialize input buffer pointer
rxd_buf_ptr:	.word	input_buf	;initialize receiver buffer pointer
input_buf_count: .word	0		;initialize # of characters in buffer
rxd_buf_count:	.word	0		;initialize # of buffered characters
output_buf_ptr:	.word	output_buf	;initialize output buffer pointer
txd_buf_ptr:	.word	output_buf	;initialize xmitter buffer pointer
output_buf_count: .word	0		;initialize # of characters in buffer

		.vectors

		.word	unexpected_interrupt  ;nonmaskable interrupt
		.word	c6502rt_startup	      ;reset
		.word	serial_interrupt      ;interrupt request

		.program

;	Entry point for routines to use the monitor I/O drivers.

		.iftrue	MONITOR_INTERFACE=YES

		.global	c6502rt_entry

c6502rt_entry:	jmp	c6502rt_functions

		.endif

;	Control C signal reset routine

unexpected_interrupt:  .equal  $
ctrl_c_reset:	sei				;disable all interrupts

		.iftrue	MEMORY_TEST=YES

		lda	#1			;disable the memory mapper
		sta	map_enable
		ldy	#0			;clear 'run memory test' flag
		jmp	skip_mem_test

		.endif

;	Normal startup routine

		.global	c6502rt_startup
		.global	end

c6502rt_startup:  .equal  $

		.iftrue	MEMORY_TEST=YES

		ldy	#1			;set 'run memory test' flag

		.endif

skip_mem_test:	cld				;the compiler uses binary mode
		ldx	#STACK			;init stack pointer
		txs

;	Set up the S6551 Serial Port chip.

		lda	#00011110b		;1 stop, 8 data, 9600 baud
		sta	serial_control
		lda	#00000101b		;no parity, interrupts enabled
		sta	serial_command
		lda	serial_status		;do dummy read to clear status

;	Test the memory and memory mapper if MEMORY_TEST is set to 1.

		.iftrue	MEMORY_TEST=YES

		lda	#0			;map in RAM from 0000 - 0fff
		sta	map_8_11
		lda	#2			;1st RAM bank is bank #2
		sta	map_addr
		lda	#1			;map in ROM from 1000 - 1fff
		sta	map_8_11
		lda	#0			;ROM is bank #0
		sta	map_addr+1
		lda	#2			;map in ROM from 2000 - 2fff
		sta	map_8_11
		lda	#0			;ROM is bank #0
		sta	map_addr+2
		lda	#dh			;map in ROM from d000 - dfff
		sta	map_8_11		; which is where this code
		lda	#0			; resides
		sta	map_addr+13
		lda	#0			;enable the mapper
		sta	map_enable
		cpy	#0			;check 'run memory test' flag
		beq	?skip_mem_test2		;don't rerun the tests if = 0
		jsr	memory_test		;execute the memory test

;	Map in RAM from 0000 - cfff,  ROM from d000 - ffff.
;	RAM is already mapped in from 0000 - 0fff.
;	ROM is already mapped in from d000 - dfff.

?skip_mem_test2:lda	#1h			;map 1000 - 1fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+1		;store bank #
		lda	#2h			;map 2000 - 2fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+2		;store bank #
		lda	#3h			;map 3000 - 3fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+3		;store bank #
		lda	#4h			;map 4000 - 4fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+4		;store bank #
		lda	#5h			;map 5000 - 5fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+5		;store bank #
		lda	#6h			;map 6000 - 6fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+6		;store bank #
		lda	#7h			;map 7000 - 7fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+7		;store bank #
		lda	#8h			;map 8000 - 8fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+8		;store bank #
		lda	#9h			;map 9000 - 9fff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+9		;store bank #
		lda	#ah			;map a000 - afff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+10		;store bank #
		lda	#bh			;map b000 - bfff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+11		;store bank #
		lda	#ch			;map c000 - cfff
		sta	map_8_11
		lda	#2			;RAM is in bank 2
		sta	map_addr+12		;store bank #
		lda	#eh			;map e000 - efff
		sta	map_8_11
		lda	#0			;ROM is in bank 0
		sta	map_addr+14		;store bank #
		lda	#fh			;map f000 - ffff
		sta	map_8_11
		lda	#0			;ROM is in bank 0
		sta	map_addr+15		;store bank #

		.endif

;	move initialized data into ram

		sec
		lda	#.low.init_data_end_addr ;get size of init data
		sbc	#.low.init_data_addr
		sta	__oper1
		lda	#.high.init_data_end_addr
		sbc	#.high.init_data_addr
		sta	__oper1+1
		ora	__oper1
		beq	?no_init_data		;skip move if size = 0
		lda	#.low.const_data_end_addr ;load addr of init data
		sta	__oper2
		lda	#.high.const_data_end_addr
		sta	__oper2+1
		lda	#.low.init_data_addr	;load runtime address
		sta	__addr_reg
		lda	#.high.init_data_addr
		sta	__addr_reg+1
		ldy	#0			;init index register
?init_loop:	lda	(__oper2),y		;move byte to destination
		sta	(__addr_reg),y
		clc
		lda	__oper2			;increment source ptr
		adc	#1
		sta	__oper2
		lda	__oper2+1
		adc	#0
		sta	__oper2+1
		clc
		lda	__addr_reg		;increment destination ptr
		adc	#1
		sta	__addr_reg
		lda	__addr_reg+1
		adc	#0
		sta	__addr_reg+1
		sec
		lda	__oper1			;decrement byte count
		sbc	#1
		sta	__oper1
		lda	__oper1+1
		sbc	#0
		sta	__oper1+1
		ora	__oper1
		bne	?init_loop		;loop till init data is moved

;	zero uninitialized data area

?no_init_data:	sec
		lda	#.low.uninit_data_end_addr ;get uninit data size
		sbc	#.low.uninit_data_addr
		sta	__oper1
		lda	#.high.uninit_data_end_addr
		sbc	#.high.uninit_data_addr
		sta	__oper1+1
		ora	__oper1
		beq	?no_uninit_data		;skip initialization if = 0
		lda	#.low.uninit_data_addr	;load uninit data starting addr
		sta	__addr_reg
		lda	#.high.uninit_data_addr
		sta	__addr_reg+1
		ldy	#0			;init index
?uninit_loop:	tya
		sta	(__addr_reg),y		;zero uninitialized data area
		clc
		lda	__addr_reg		;increment destination address
		adc	#1
		sta	__addr_reg
		lda	__addr_reg+1
		adc	#0
		sta	__addr_reg+1
		sec
		lda	__oper1			;decrement byte count
		sbc	#1
		sta	__oper1
		lda	__oper1+1
		sbc	#0
		sta	__oper1+1
		ora	__oper1
		bne	?uninit_loop		;loop till byte count = 0
?no_uninit_data: .equal $

;	Allocate the library temporary storage space and set up the
;	compiler stack.

		sec
		lda	#.low.COMPILER_STACK	;allocate temporary storage and
		sbc	#.low.lib_temp_constants_size ; set compiler stack ptr
		sta	__lib_temp_ptr
		sta	__stack_ptr
		lda	#.high.COMPILER_STACK
		sbc	#.high.lib_temp_constants_size
		sta	__lib_temp_ptr+1
		sta	__stack_ptr+1

		cli				;enable interrupts
		jsr	_main			;execute program

;	Here to loop when using the simulator so that the output buffer
;	can empty.

		.iftrue	SIMULATOR=YES

?int_empty_loop:  .equal  $
		lda	output_buf_count	;load output buffer count
		ora	output_buf_count+1
		bne	?int_empty_loop		;loop till output buffer empty

		.endif

end:		jmp	c6502rt_startup	

		.page

		.init_data

		.global	__standard_in
		.global	__standard_out
		.global	__output_table
		.global	__input_table
		.global	__unget_table

__standard_in:	.word	0		;init to serial input port
__standard_out:	.word	0		;init to serial output port

__output_table:	.word	serial_out	;address of serial port output routine

__input_table:	.word	serial_in	;address of serial port input routine
end_of_input_table: .equ  $

__unget_table:	.blkb	((end_of_input_table-__input_table)/2)*2,0

		.page

;	Return to the calling program with the character in register a.

		.program

serial_in:	.equal	$
?empty_loop:	php			;save interrupt enable flag
		sei			;disable interrupts
		lda	input_buf_count ;load character count
		ora	input_buf_count+1
		bne	?not_empty
		plp			;restore interrupt enable flag
		jmp	?empty_loop	;wait for character if empty
?not_empty:	lda	input_buf_count	;decrement character count
		sec
		sbc	#1
		sta	input_buf_count
		lda	input_buf_count+1
		sbc	#0
		sta	input_buf_count+1
		plp			;restore interrupt enable flag
		lda	input_buf_ptr	;load input buffer ptr
		sta	__addr_reg
		lda	input_buf_ptr+1
		sta	__addr_reg+1
		ldy	#0
		lda	(__addr_reg),y	;load character
		tay			;save character in y
		lda	__addr_reg	;increment input buffer ptr
		clc
		adc	#1
		sta	input_buf_ptr
		lda	__addr_reg+1
		adc	#0
		sta	input_buf_ptr+1
		lda	input_buf_ptr	;check for at end of buffer
		sec
		sbc	#.low.input_buf+INPUT_BUF_SIZE
		sta	__oper1
		lda	input_buf_ptr+1
		sbc	#.high.input_buf+INPUT_BUF_SIZE
		ora	__oper1
		bne	?not_at_end	;not at end of buffer if not = 0
		lda	#.low.input_buf	;init buffer ptr to start of buffer
		sta	input_buf_ptr
		lda	#.high.input_buf
		sta	input_buf_ptr+1
?not_at_end:	tya			;xfer character -> a
		rts

		.page

;	Store the character in 'a' in the output buffer.

serial_out:	cmp	#LF
		bne	?not_lf
		lda	#CR
		jsr	store_character
		lda	#LF
?not_lf:	jsr	store_character
		rts

store_character:  .equal  $
		pha			;save character to output
?full_loop:	sei			;disable interrupts
		lda	output_buf_count ;check for output buffer full
		sec
		sbc	#.low.OUTPUT_BUF_SIZE
		sta	__oper1
		lda	output_buf_count+1
		sbc	#.high.OUTPUT_BUF_SIZE
		ora	__oper1
		bne	?not_full
		cli			;enable interrupts
		jmp	?full_loop	;loop till a char is xmitted if full
?not_full:	lda	output_buf_ptr	;load output buffer ptr
		sta	__oper1
		lda	output_buf_ptr+1
		sta	__oper1+1
		pla			;return character to output
		pha			;save it again
		ldy	#0
		sta	(__oper1),y	;store character in output buffer
		lda	__oper1		;increment output buffer ptr
		clc
		adc	#1
		sta	output_buf_ptr
		lda	__oper1+1
		adc	#0
		sta	output_buf_ptr+1
		lda	output_buf_ptr	;check for at end of buffer
		sec
		sbc	#.low.output_buf+OUTPUT_BUF_SIZE
		sta	__oper1
		lda	output_buf_ptr+1
		sbc	#.high.output_buf+OUTPUT_BUF_SIZE
		ora	__oper1
		bne	?not_at_end
		lda	#.low.output_buf ;init ptr to start of buffer
		sta	output_buf_ptr
		lda	#.high.output_buf
		sta	output_buf_ptr+1
?not_at_end:	lda	output_buf_count ;increment character count
		clc
		adc	#1
		sta	output_buf_count
		lda	output_buf_count+1
		adc	#0
		sta	output_buf_count+1
		cli			;enable interrupts
		lda	output_buf_empty ;check buffer empty flag
		beq	?not_empty	;don't set tie flag if empty flag = 0
		lda	#0
		sta	output_buf_empty ;clear empty flag
		lda	serial_command	;set tie flag to start transmission
		ora	#txd_int_enable
		sta	serial_command
?not_empty:	pla			;restore character to output
		rts

		.page

;	Serial port interrupt service routine.

serial_interrupt:  .equal  $
		pha			;save environment
		tya
		pha
		txa
		pha
		lda	__oper1+1
		pha
		lda	__oper1
		pha

;	Input a character if the rdrf bit is set. Process backspace, signal
;	characters, character echoing  and carriage returns if __raw_mode = 0.

		lda	serial_status
		and	#rxd_error	;check for framing error, overrun or
		beq	?not_rxd_err	; parity error
		lda	rxd		;clear framing error
		jmp	?xmit_check
?not_rxd_err:	lda	serial_status
		and	#rxd_ready	;check for character ready
		bne	?receiver_full
		jmp	?xmit_check
?receiver_full:	lda	__raw_mode	;check for raw mode
		beq	?not__raw_mode	;process characters if _raw_mode = 0
		lda	rxd		;load character
		jmp	?raw_mode	;skip character processing
?not__raw_mode:	lda	rxd		;load character

		cmp	#XON		;skip Ctrl-S & Ctrl-Q
		bne	?not_xon
		jmp	?xmit_check
?not_xon:	cmp	#XOFF
		bne	?not_xoff
		jmp	?xmit_check

;	Process backspace

?not_xoff:	cmp	#BS
		beq	?bs
		jmp	?not_bs
?bs:		lda	rxd_buf_count	;ld character per line count
		ora	rxd_buf_count+1
		bne	?not_empty	;delete last character if not empty
		jmp	?xmit_check	;else just ignore the backspace
?not_empty:	lda	rxd_buf_count	;decrement character count
		sec	
		sbc	#1
		sta	rxd_buf_count
		lda	rxd_buf_count+1
		sbc	#0
		sta	rxd_buf_count+1
		lda	rxd_buf_ptr	;check for buffer ptr at start of buffer
		sec
		sbc	#.low.input_buf
		sta	__oper1
		lda	rxd_buf_ptr+1
		sbc	#.high.input_buf
		ora	__oper1
		bne	?not_at_start
		lda	#.low.input_buf+INPUT_BUF_SIZE ;load addr of end of buf
		sta	rxd_buf_ptr
		lda	#.high.input_buf+INPUT_BUF_SIZE
		sta	rxd_buf_ptr+1
?not_at_start:	lda	rxd_buf_ptr	;decrement buffer pointer
		sec
		sbc	#1
		sta	rxd_buf_ptr
		lda	rxd_buf_ptr+1
		sbc	#0
		sta	rxd_buf_ptr+1
		lda	output_buf_empty ;check for output buffer empty
		beq	?bs_loop1	;wait for tdre to go high before sending
		lda	#0		;else clear flag & start transmit
		sta	output_buf_empty
		lda	serial_command	;set transmitter interrupt flag 
		ora	#txd_int_enable
		sta	serial_command
?bs_loop1:	lda	serial_status
		and	#txd_ready	;wait for transmitter ready
		beq	?bs_loop1
?9:		lda	#BS		;echo backspace
		sta	txd
?bs_loop2:	lda	serial_status
		and	#txd_ready	;wait for transmitter ready
		beq	?bs_loop2
		lda	#' '		;echo space
		sta	txd
?bs_loop3:	lda	serial_status
		and	#txd_ready	;wait for transmitter ready
		beq	?bs_loop3
		lda	#BS		;echo backspace
		sta	txd
		jmp	?xmit_check

;	Process signal characters

?not_bs:	cmp	#CTRL_C
		bne	?not_signal
		lda	_ctrl_c_signal ;load ptr to signal routine addr
		sta	__addr_reg
		lda	_ctrl_c_signal+1
		sta	__addr_reg+1
		jsr	__indirect_call	;execute the signal handler
		jmp	?xmit_check

;	Process carriage returns

?not_signal:	cmp	#CR
		bne	?not_cr
		lda	#LF		;replace carriage return with line feed

;	Store the character

?raw_mode: 	.equal	$
?not_cr:	tax			;xfer character -> x
		lda	rxd_buf_ptr	;load receiver buffer ptr
		sta	__oper1
		lda	rxd_buf_ptr+1
		sta	__oper1+1
		txa			;xfer character -> a
		ldy	#0
		sta	(__oper1),y	;store character in input buffer
		lda	__oper1		;increment input buffer ptr
		clc
		adc	#1
		sta	rxd_buf_ptr
		lda	__oper1+1
		adc	#0
		sta	rxd_buf_ptr+1
		lda	rxd_buf_ptr	;check for at end of buffer
		sec
		sbc	#.low.input_buf+INPUT_BUF_SIZE
		sta	__oper1
		lda	rxd_buf_ptr+1
		sbc	#.high.input_buf+INPUT_BUF_SIZE
		ora	__oper1
		bne	?not_in_buf_end
		lda	#.low.input_buf	;load addr of start of buffer
		sta	rxd_buf_ptr
		lda	#.high.input_buf
		sta	rxd_buf_ptr+1

;	If processing characters, store up an entire line before telling the
;	routine that actually reads the characters from the buffer that
;	anything is in the buffer.  This is done by keeping a separate
;	character counter.

?not_in_buf_end:lda	__raw_mode	;check for raw mode
		beq	?not_raw_mode_1

;	Here to just store the character in raw mode.

		lda	input_buf_count	;increment character count
		clc
		adc	#1
		sta	input_buf_count
		lda	input_buf_count+1
		adc	#0
		sta	input_buf_count+1
		lda	#0
		sta	rxd_buf_count	;zero character per line count in case
		sta	rxd_buf_count+1	; modes are switched
		jmp	?xmit_check

;	Here to store the character when not in raw mode.

?not_raw_mode_1:  .equal  $
		lda	rxd_buf_count	;increment character per line count
		clc
		adc	#1
		sta	rxd_buf_count
		lda	rxd_buf_count+1
		adc	#0
		sta	rxd_buf_count+1

;	Echo the character

		lda	output_buf_empty ;check for output buffer empty
		beq	?echo_loop1	;wait for tdre to go high before sending
		lda	#0		;else clear flag & start transmitter
		sta	output_buf_empty
		lda	serial_command	;set transmitter interrupt enable
		ora	#txd_int_enable
		sta	serial_command
?echo_loop1:	lda	serial_status	;wait till transmitter becomes ready
		and	#txd_ready
		beq	?echo_loop1
		txa			;xfer character -> a
		cmp	#LF
		bne	?not_lf
		lda	#CR		;echo carriage return line feed pair
		sta	txd

;	Since the character was a carriage return (which was converted to
;	a line feed up above) set the input buffer character count equal to
;	the character per line count.

		lda	rxd_buf_count	;get the total # of chars in buffer	
		clc
		adc	input_buf_count
		sta	input_buf_count
		lda	rxd_buf_count+1
		adc	input_buf_count+1
		sta	input_buf_count+1
		lda	#0		;clear char per line count
		sta	rxd_buf_count
		sta	rxd_buf_count+1

?echo_loop2:	lda	serial_status	;loop till transmitter ready
		and	#txd_ready
		beq	?echo_loop2
		lda	#LF
?not_lf:	sta	txd		;echo character

;	Output a character if the tdre bit is set and there is a character
;	to output.

?xmit_check:	lda	serial_status	;check for transmitter empty
		and	#txd_ready
		beq	?xmit_not_rdy
		lda	output_buf_count ;check character count
		ora	output_buf_count+1
		bne	?send_nxt_char	;send next character if not empty
		lda	#1		;set buffer empty flag, which allows
		sta	output_buf_empty ; the routine that stores the char
				 	 ; to set tie
		lda	serial_command	;clear tie for now
		and	#.not.txd_int_enable
		sta	serial_command
		jmp	?done
?send_nxt_char:	lda	output_buf_count ;decrement character count
		sec
		sbc	#1
		sta	output_buf_count
		lda	output_buf_count+1
		sbc	#0
		sta	output_buf_count+1
		lda	txd_buf_ptr	;load output buffer ptr
		sta	__oper1
		lda	txd_buf_ptr+1
		sta	__oper1+1
		ldy	#0	
		lda	(__oper1),y	;load character to output
		sta	txd		;xmit it
		lda	__oper1		;increment output buffer ptr
		clc
		adc	#1
		sta	txd_buf_ptr
		lda	__oper1+1
		adc	#0
		sta	txd_buf_ptr+1
		lda	txd_buf_ptr	;check for at end of buffer
		sec
		sbc	#.low.output_buf+OUTPUT_BUF_SIZE
		sta	__oper1
		lda	txd_buf_ptr+1
		sbc	#.high.output_buf+OUTPUT_BUF_SIZE
		ora	__oper1
		bne	?not_buf_end
		lda	#.low.output_buf ;load addr of start of buffer
		sta	txd_buf_ptr
		lda	#.high.output_buf
		sta	txd_buf_ptr+1
?not_buf_end:	.equal	$
?xmit_not_rdy:	.equal	$
?done:		pla			;restore environment
		sta	__oper1
		pla
		sta	__oper1+1
		pla
		tax
		pla
		tay
		pla
		rti

		.page

;	Input buffer flush routine.	

		.global	__flush_input_buffer

__flush_input_buffer:  .equal  $
		php			;save interrupt enables
		sei			;disable interrupts
		lda	#0		;zero character count
		sta	input_buf_count
		sta	input_buf_count+1
		sta	rxd_buf_count	;zero receiver char count
		sta	rxd_buf_count+1
		lda	#.low.input_buf	;set buffer ptr's to start of buffers
		sta	rxd_buf_ptr
		sta	input_buf_ptr
		lda	#.high.input_buf
		sta	rxd_buf_ptr+1
		sta	input_buf_ptr+1
		plp			;restore interrupt enables
		rts

		.page

;	This routine handles monitor calls from external routines.  The
;	routine function is in 'x'.  The following functions are implemented,
;	with the corresponding 'x' value.

;	0 - Input a character from the serial port.  Return the character
;	    in register a.
;	1 - Output a character to the serial port.  The character to output
;	    is in register a.

		.init_data

funct_table:	.word	serial_in	;serial input routine
		.word	serial_out	;serial output routine

		.program

c6502rt_functions:  .equal  $
		pha			;save a
		txa			;xfer function # -> a
		asl	a		;multiply function # by 2
		clc
		adc	#.low.funct_table ;get index to table entry
		sta	__addr_reg
		lda	#.high.funct_table
		adc	#0
		sta	__addr_reg+1
		ldy	#0
		lda	(__addr_reg),y	;load routine address lsb
		tax
		iny
		lda	(__addr_reg),y	;load routine address msb
		sta	__addr_reg+1	;store routine address msb
		stx	__addr_reg	;store routine address lsb
		pla			;restore a
		jmp	__indirect_call	;execute routine


		.global	__interrupt_entry
		.global	__interrupt_exit

;	Interrupt function entry code.
;	Save the registers and the page 0 registers and allocate a new area
;	for the library temporaries.

__interrupt_entry:  .equal  $
		pha				;save the registers
		tya
		pha
		txa
		pha
		tsx				;xfer ptr to stack -> x
		lda	__oper1+1		;save the page 0 operands
		pha
		lda	__oper1
		pha
		lda	__oper2+1
		pha
		lda	__oper2
		pha
		lda	__addr_reg+1
		pha
		lda	__addr_reg
		pha
		lda	__lib_temp_ptr+1	;save the lib temporary ptr
		pha
		lda	__lib_temp_ptr
		pha

		.if	 BANK_SWITCHING=YES
		lda	__oper1_high8
		pha
		lda	__oper2_high8
		pha
		.endif

		lda	__stack_ptr		;allocate new lib temps
		sec
		sbc	#.low.lib_temp_constants_size
		sta	__stack_ptr
		lda	__stack_ptr+1
		sbc	#.high.lib_temp_constants_size
		sta	__stack_ptr+1
		lda	STACK_PAGE.or.5h,x	;load return address msb
		pha				;put the destination msb on stk
		lda	STACK_PAGE.or.4h,x	;load return address lsb
		pha				;put the destination lsb on stk
		rts

;	Interrupt function exit code.
;	This routine is jumped to.  The last item on the stack after all
;	the saved state has been restored is the return address of the 
;	interrupted routine.

__interrupt_exit:  .equal  $
		lda	__stack_ptr		;deallocate temporaries
		clc
		adc	#.low.lib_temp_constants_size
		sta	__stack_ptr
		lda	__stack_ptr+1
		adc	#.high.lib_temp_constants_size
		sta	__stack_ptr+1

		.if	 BANK_SWITCHING=YES
		pla
		sta	__oper2_high8
		pla
		sta	__oper1_high8
		.endif

		pla				;restore lib temporary ptr
		sta	__lib_temp_ptr
		pla
		sta	__lib_temp_ptr+1
		pla				;restore page 0 operands
		sta	__addr_reg
		pla
		sta	__addr_reg+1
		pla
		sta	__oper2
		pla
		sta	__oper2+1
		pla
		sta	__oper1
		pla
		sta	__oper1+1
		pla				;restore the registers
		tax
		pla
		tay
		pla
		sta	__temp_a_reg            ;use A reg to clear stack
		pla				;clear interrupt function return
		pla				; address off stack
		lda	__temp_a_reg		;restore the A register

		rti				;return to interrupted routine

		.iftrue	MEMORY_TEST=YES

		.include	c6502mt.src	;assemble memory test code

		.endif

		.program

		.end	c6502rt_startup





